package nachos.threads;

import nachos.machine.*;

/**
 * A <tt>Lock</tt> is a synchronization primitive that has two states, <i>busy</i> and <i>free</i>.
 * There are only two operations allowed on a lock:
 * 
 * <ul>
 * <li><tt>acquire()</tt>: atomically wait until the lock is <i>free</i> and then set it to
 * <i>busy</i>.
 * <li><tt>release()</tt>: set the lock to be <i>free</i>, waking up one waiting thread if possible.
 * </ul>
 * 
 * <p>
 * Also, only the thread that acquired a lock may release it. As with semaphores, the API does not
 * allow you to read the lock state (because the value could change immediately after you read it).
 */
public class Lock {
  /**
   * Allocate a new lock. The lock will initially be <i>free</i>.
   */
  public Lock() {
  }

  /**
   * Atomically acquire this lock. The current thread must not already hold this lock.
   */
  public void acquire() {
    Lib.assertTrue(!isHeldByCurrentThread());

    boolean intStatus = Machine.interrupt().disable();
    KThread thread = KThread.currentThread();

    if (lockHolder != null) {
      waitQueue.waitForAccess(thread);
      KThread.sleep();
    }
    else {
      waitQueue.acquire(thread);
      lockHolder = thread;
    }

    Lib.assertTrue(lockHolder == thread);

    Machine.interrupt().restore(intStatus);
  }

  /**
   * Atomically release this lock, allowing other threads to acquire it.
   */
  public void release() {
    Lib.assertTrue(isHeldByCurrentThread());

    boolean intStatus = Machine.interrupt().disable();

    if ((lockHolder = waitQueue.nextThread()) != null)
      lockHolder.ready();

    Machine.interrupt().restore(intStatus);
  }

  /**
   * Test if the current thread holds this lock.
   * 
   * @return true if the current thread holds this lock.
   */
  public boolean isHeldByCurrentThread() {
    return (lockHolder == KThread.currentThread());
  }

  private KThread lockHolder = null;
  private ThreadQueue waitQueue = ThreadedKernel.scheduler.newThreadQueue(true);
}
